//
//  YXVideoCell.m
//  YXImageBrowserDemo
//
//  Created by guo peng huang on 2021/5/19.
//

#import "YXVideoCell.h"
#import "YXVideoData.h"
#import "YXVideoData+Internal.h"
#import "YXCopywriter.h"
#import "YXIconManager.h"
#import <objc/runtime.h>
#import "YXVideoCell+Internal.h"


@interface NSObject (YXVideoPlayingRecord)
- (void)yx_videoPlayingAdd:(NSObject *)obj;
- (void)yx_videoPlayingRemove:(NSObject *)obj;
- (BOOL)yx_noVideoPlaying;
@end
@implementation NSObject (YXVideoPlayingRecord)
- (NSMutableSet *)yx_videoPlayingSet {
    static void *kRecordKey = &kRecordKey;
    NSMutableSet *set = objc_getAssociatedObject(self, kRecordKey);
    if (!set) {
        set = [NSMutableSet set];
        objc_setAssociatedObject(self, kRecordKey, set, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return set;
}
- (void)yx_videoPlayingAdd:(NSObject *)obj {
    [[self yx_videoPlayingSet] addObject:[NSString stringWithFormat:@"%p", obj]];
}
- (void)yx_videoPlayingRemove:(NSObject *)obj {
    [[self yx_videoPlayingSet] removeObject:[NSString stringWithFormat:@"%p", obj]];
}
- (BOOL)yx_noVideoPlaying {
    return [self yx_videoPlayingSet].count == 0;
}
@end

@interface YXVideoCell ()<YXVideoDataDelegate, YXVideoViewDelegate, UIGestureRecognizerDelegate>

@end

@implementation YXVideoCell {
    CGPoint _interactStartPoint;
    BOOL _interacting;
}

- (void)dealloc {
    [self.yx_backView yx_videoPlayingRemove:self];
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self initValue];
        [self.contentView addSubview:self.videoView];
        /// 在 layoutSubviews 方法里面赋值frame frame 视图会跳动  不会原路返回
        self.videoView.frame = self.bounds;
        [self addGesture];
    }
    return self;
}

- (void)initValue {
    _interactStartPoint = CGPointZero;
    _interacting = NO;
}

/// 重写 防止cell数据复用
- (void)prepareForReuse {
    ((YXVideoData *)self.yx_cellData).delegate = nil;
    self.videoView.thumbImageView.image = nil;
    [self hideAuxiliaryView];
    [self.videoView reset];
    self.videoView.asset = nil;
    [super prepareForReuse];
}


#pragma mark - private

- (void)hideAuxiliaryView {
    [self.yx_auxiliaryViewHandler() yx_hideLoadingWithContainer:self];
    [self.yx_auxiliaryViewHandler() yx_hideToastWithContainer:self];
}

- (void)updateImageLayoutWithOrientation:(UIDeviceOrientation)orientation previousImageSize:(CGSize)previousImageSize {
    YXVideoData *data = self.yx_cellData;
    UIImage *image = self.videoView.thumbImageView.image;
    CGSize imageSize = image.size;
    
    CGRect imageViewFrame = [data yx_imageViewFrameWithContainerSize:self.yx_containerSize(orientation) imageSize:imageSize orientation:orientation];
    
    CGFloat scale;
    if (previousImageSize.width > 0 && previousImageSize.height > 0) {
        scale = imageSize.width / imageSize.height - previousImageSize.width / previousImageSize.height;
    } else {
        scale = 0;
    }
    // '0.001' is admissible error.
    if (ABS(scale) <= 0.001) {
        self.videoView.thumbImageView.frame = imageViewFrame;
    } else {
        [UIView animateWithDuration:0.25 animations:^{
            self.videoView.thumbImageView.frame = imageViewFrame;
        }];
    }
}

- (void)hideBrowser {
    ((YXVideoData *)self.yx_cellData).delegate = nil;
    self.videoView.thumbImageView.hidden = NO;
    self.videoView.autoPlayCount = 0;
    [self.videoView reset];
    [self.videoView hideToolBar:YES];
    [self.videoView hidePlayButton];
    self.yx_hideBrowser();
    _interacting = NO;
}

- (void)hideToolViews:(BOOL)hide {
    if (hide) {
        self.yx_hideToolViews(YES);
    } else {
        if ([self.yx_backView yx_noVideoPlaying]) {
            self.yx_hideToolViews(NO);
        }
    }
}

#pragma mark - <YXCellProtocol>

@synthesize yx_currentOrientation = _yx_currentOrientation;
@synthesize yx_containerSize = _yx_containerSize;
@synthesize yx_backView = _yx_backView;
@synthesize yx_collectionView = _yx_collectionView;
@synthesize yx_isTransitioning = _yx_isTransitioning;
@synthesize yx_auxiliaryViewHandler = _yx_auxiliaryViewHandler;
@synthesize yx_hideStatusBar = _yx_hideStatusBar;
@synthesize yx_hideBrowser = _yx_hideBrowser;
@synthesize yx_hideToolViews = _yx_hideToolViews;
@synthesize yx_cellData = _yx_cellData;
@synthesize yx_currentPage = _yx_currentPage;
@synthesize yx_selfPage = _yx_selfPage;
@synthesize yx_cellIsInCenter = _yx_cellIsInCenter;
@synthesize yx_isRotating = _yx_isRotating;

- (void)setYx_cellData:(id<YXDataProtocol>)yx_cellData {
    _yx_cellData = yx_cellData;
    YXVideoData *data = (YXVideoData *)yx_cellData;
    data.delegate = self;
    
    UIDeviceOrientation orientation = self.yx_currentOrientation();
    CGSize containerSize = self.yx_containerSize(orientation);
    [self.videoView updateLayoutWithExpectOrientation:orientation containerSize:containerSize];
    self.videoView.autoPlayCount = data.autoPlayCount;
    self.videoView.topBar.cancelButton.hidden = data.shouldHideForkButton;
}

- (void)yx_orientationWillChangeWithExpectOrientation:(UIDeviceOrientation)orientation {
    if (_interacting) [self restoreGestureInteractionWithDuration:0];
}

- (void)yx_orientationChangeAnimationWithExpectOrientation:(UIDeviceOrientation)orientation {
    [self updateImageLayoutWithOrientation:orientation previousImageSize:self.videoView.thumbImageView.image.size];
    CGSize containerSize = self.yx_containerSize(orientation);
    [self.videoView updateLayoutWithExpectOrientation:orientation containerSize:containerSize];
}

- (UIView *)yx_foregroundView {
    return self.videoView.thumbImageView;
}

- (void)yx_pageChanged {
    if (self.yx_currentPage() != self.yx_selfPage()) {
        [self.videoView reset];
        [self hideToolViews:NO];
        [self.yx_auxiliaryViewHandler() yx_hideLoadingWithContainer:self];
        if (_interacting) [self restoreGestureInteractionWithDuration:0];
        self.videoView.needAutoPlay = NO;
    } else {
        self.videoView.needAutoPlay = YES;
    }
}

#pragma mark - <YXVideoDataDelegate>

- (void)yx_startLoadingAVAssetFromPHAssetForData:(YXVideoData *)data {}

- (void)yx_finishLoadingAVAssetFromPHAssetForData:(YXVideoData *)data {}

- (void)yx_startLoadingFirstFrameForData:(YXVideoData *)data {
    if (!self.videoView.thumbImageView.image) {
        [self.yx_auxiliaryViewHandler() yx_showLoadingWithContainer:self];
    }
}

- (void)yx_finishLoadingFirstFrameForData:(YXVideoData *)data {
    [self.yx_auxiliaryViewHandler() yx_hideLoadingWithContainer:self];
}

- (void)yx_videoData:(YXVideoData *)data downloadingWithProgress:(CGFloat)progress {
    [self.yx_auxiliaryViewHandler() yx_showLoadingWithContainer:self progress:progress];
}

- (void)yx_finishDownloadingForData:(YXVideoData *)data {
    [self.yx_auxiliaryViewHandler() yx_hideLoadingWithContainer:self];
}

- (void)yx_videoData:(YXVideoData *)data readyForAVAsset:(AVAsset *)asset {
    self.videoView.asset = asset;
}

- (void)yx_videoData:(YXVideoData *)data readyForThumbImage:(UIImage *)image {
    if (!self.videoView.isPlaying) {
        self.videoView.thumbImageView.hidden = NO;
    }
    
    if (!self.videoView.thumbImageView.image) {
        CGSize previousSize = self.videoView.thumbImageView.image.size;
        self.videoView.thumbImageView.image = image;
        [self updateImageLayoutWithOrientation:self.yx_currentOrientation() previousImageSize:previousSize];
    }
}

- (void)yx_videoIsInvalidForData:(YXVideoData *)data {
    [self.yx_auxiliaryViewHandler() yx_hideLoadingWithContainer:self];
    NSString *imageIsInvalid = [YXCopywriter sharedCopywriter].videoIsInvalid;
    if (self.videoView.thumbImageView.image) {
        [self.yx_auxiliaryViewHandler() yx_showIncorrectToastWithContainer:self text:imageIsInvalid];
    } else {
        [self.yx_auxiliaryViewHandler() yx_showLoadingWithContainer:self text:imageIsInvalid];
    }
}

#pragma mark - <YXVideoViewDelegate>

- (BOOL)yx_isFreezingForVideoView:(YXVideoView *)view {
    return self.yx_isTransitioning();
}

- (void)yx_preparePlayForVideoView:(YXVideoView *)view {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if (!view.isPlaying && !view.isPlayFailed && self.yx_selfPage() == self.yx_currentPage()) {
            [self.yx_auxiliaryViewHandler() yx_showLoadingWithContainer:self];
        }
    });
}

- (void)yx_startPlayForVideoView:(YXVideoView *)view {
    self.videoView.thumbImageView.hidden = YES;
    [self.yx_backView yx_videoPlayingAdd:self];
    [self.yx_auxiliaryViewHandler() yx_hideLoadingWithContainer:self];
    [self hideToolViews:YES];
}

- (void)yx_didPlayToEndTimeForVideoView:(YXVideoView *)view {
    YXVideoData *data = (YXVideoData *)self.yx_cellData;
    if (data.repeatPlayCount == NSUIntegerMax) {
        [view preparPlay];
    } else if (data.repeatPlayCount > 0) {
        --data.repeatPlayCount;
        [view preparPlay];
    } else {
        [self hideToolViews:NO];
    }
}

- (void)yx_finishPlayForVideoView:(YXVideoView *)view {
    [self.yx_backView yx_videoPlayingRemove:self];
    [self hideToolViews:NO];
}

- (void)yx_playFailedForVideoView:(YXVideoView *)view {
    [self.yx_auxiliaryViewHandler() yx_hideLoadingWithContainer:self];
    [self.yx_auxiliaryViewHandler() yx_showIncorrectToastWithContainer:self text:YXCopywriter.sharedCopywriter.videoError];
}

- (void)yx_respondsToTapGestureForVideoView:(YXVideoView *)view {
    if (self.yx_isRotating()) return;
    
    YXVideoData *data = self.yx_cellData;
    if (data.singleTouchBlock) {
        data.singleTouchBlock(data);
    } else {
        [self hideBrowser];
    }
}

- (void)yx_cancelledForVideoView:(YXVideoView *)view {
    if (self.yx_isRotating()) return;
    
    [self hideBrowser];
}

- (CGSize)yx_containerSizeForVideoView:(YXVideoView *)view {
    return self.yx_containerSize(self.yx_currentOrientation());
}

- (void)yx_autoPlayCountChanged:(NSUInteger)count {
    YXVideoData *data = (YXVideoData *)self.yx_cellData;
    data.autoPlayCount = count;
}

#pragma mark - <UIGestureRecognizerDelegate>

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

#pragma mark - gesture

- (void)addGesture {
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToPanGesture:)];
    panGesture.cancelsTouchesInView = NO;
    panGesture.delegate = self;
    [self.videoView.tapGesture requireGestureRecognizerToFail:panGesture];
    [self.videoView addGestureRecognizer:panGesture];
}

- (void)respondsToPanGesture:(UIPanGestureRecognizer *)pan {
    if (self.yx_isRotating()) return;
    if ((!self.videoView.thumbImageView.image && !self.videoView.isPlaying)) return;
    
    YXInteractionProfile *profile = ((YXVideoData *)self.yx_cellData).interactionProfile;
    if (profile.disable) return;
    
    CGPoint point = [pan locationInView:self];
    CGSize containerSize = self.yx_containerSize(self.yx_currentOrientation());
    
    if (pan.state == UIGestureRecognizerStateBegan) {
        _interactStartPoint = point;
    } else if (pan.state == UIGestureRecognizerStateCancelled || pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateRecognized || pan.state == UIGestureRecognizerStateFailed) {
        
        // End
        if (_interacting) {
            CGPoint velocity = [pan velocityInView:self.videoView];
            
            BOOL velocityArrive = ABS(velocity.y) > profile.dismissVelocityY;
            BOOL distanceArrive = ABS(point.y - _interactStartPoint.y) > containerSize.height * profile.dismissScale;
            
            BOOL shouldDismiss = distanceArrive || velocityArrive;
            if (shouldDismiss) {
                [self hideBrowser];
            } else {
                [self restoreGestureInteractionWithDuration:profile.restoreDuration];
            }
        }
        
    } else if (pan.state == UIGestureRecognizerStateChanged) {
        
        if (_interacting) {
            
            // Change
            self.videoView.center = point;
            CGFloat scale = 1 - ABS(point.y - _interactStartPoint.y) / (containerSize.height * 1.2);
            if (scale > 1) scale = 1;
            if (scale < 0.35) scale = 0.35;
            self.videoView.transform = CGAffineTransformMakeScale(scale, scale);
            
            CGFloat alpha = 1 - ABS(point.y - _interactStartPoint.y) / (containerSize.height * 0.7);
            if (alpha > 1) alpha = 1;
            if (alpha < 0) alpha = 0;
            self.yx_backView.backgroundColor = [self.yx_backView.backgroundColor colorWithAlphaComponent:alpha];
            
        } else {
            
            // Start
            if (CGPointEqualToPoint(_interactStartPoint, CGPointZero) || self.yx_currentPage() != self.yx_selfPage() || !self.yx_cellIsInCenter() || self.videoView.actionBar.isTouchInside) return;
            
            CGPoint velocityPoint = [pan velocityInView:self.videoView];
            CGFloat triggerDistance = profile.triggerDistance;
            
            BOOL distanceArrive = ABS(point.y - _interactStartPoint.y) > triggerDistance && (ABS(point.x - _interactStartPoint.x) < triggerDistance && ABS(velocityPoint.x) < 500);
            
            BOOL shouldStart = distanceArrive;
            if (!shouldStart) return;
            
            [self.videoView hideToolBar:YES];
            
            _interactStartPoint = point;
            
            CGRect startFrame = self.videoView.bounds;
            CGFloat anchorX = (point.x - startFrame.origin.x) / startFrame.size.width,
            anchorY = (point.y - startFrame.origin.y) / startFrame.size.height;
            self.videoView.layer.anchorPoint = CGPointMake(anchorX, anchorY);
            self.videoView.userInteractionEnabled = NO;
            self.videoView.center = point;
            
            [self hideToolViews:YES];
            self.yx_hideStatusBar(NO);
            self.yx_collectionView().scrollEnabled = NO;
            
            _interacting = YES;
        }
    }
}

- (void)restoreGestureInteractionWithDuration:(NSTimeInterval)duration {
    [self.videoView hideToolBar:NO];
    
    CGSize containerSize = self.yx_containerSize(self.yx_currentOrientation());
    
    void (^animations)(void) = ^{
        self.yx_backView.backgroundColor = [self.yx_backView.backgroundColor colorWithAlphaComponent:1];
        
        CGPoint anchorPoint = self.videoView.layer.anchorPoint;
        self.videoView.center = CGPointMake(containerSize.width * anchorPoint.x, containerSize.height * anchorPoint.y);
        self.videoView.transform = CGAffineTransformIdentity;
    };
    void (^completion)(BOOL finished) = ^(BOOL finished){
        self.videoView.layer.anchorPoint = CGPointMake(0.5, 0.5);
        self.videoView.center = CGPointMake(containerSize.width * 0.5, containerSize.height * 0.5);
        self.videoView.userInteractionEnabled = YES;
        
        self.yx_hideStatusBar(YES);
        self.yx_collectionView().scrollEnabled = YES;
        if (!self.videoView.isPlaying) [self hideToolViews:NO];;
        
        self->_interactStartPoint = CGPointZero;
        self->_interacting = NO;
    };
    if (duration <= 0) {
        animations();
        completion(NO);
    } else {
        [UIView animateWithDuration:duration animations:animations completion:completion];
    }
}

#pragma mark - getters & setters

- (YXVideoView *)videoView {
    if (!_videoView) {
        _videoView = [[YXVideoView alloc] init];
        _videoView.delegate = self;
    }
    return _videoView;
}

@end
